with Ada.Strings.Unbounded;             use Ada.Strings.Unbounded; 

with Ada.Text_IO;                       use Ada.Text_IO;

with Ada.Strings.Unbounded.Text_IO;     use Ada.Strings.Unbounded.Text_IO;

with Ada.Containers.Indefinite_Vectors; use Ada.Containers;

with
        Unchecked_Conversion,
        System,
        Interfaces.C.Strings; 

procedure main is

    type chars_ptr_ptr is access constant Interfaces.C.Strings.chars_ptr;
    pragma Convention (C, chars_ptr_ptr);
    function To_Ptr is new Unchecked_Conversion (System.Address, chars_ptr_ptr);

    procedure execvp (
        path : Interfaces.C.Strings.chars_ptr;
        argv : chars_ptr_ptr
    );
    pragma Import (C, execvp, "execvp");
    use type Interfaces.C.size_t;

    function fork return Interfaces.C.int;
    pragma Import (C, fork, "fork");

    function wait (Status_Ptr : access Interfaces.C.int) 
    return Interfaces.C.int;
    pragma Import (C, wait, "wait");


    -- end of C function importing - access POSIX --
    --+------------------------------------------+--

    package String_Vector is new Indefinite_Vectors (Natural,String); 
    use String_Vector;

    -- split unbounded string into strings inside string vector
    function tokenize (input : in Unbounded_String) 
    return String_Vector.Vector is
        is_quoted : Boolean   := false;
        lastpos   : Positive  := 1; -- unbounded strings index at 1
        ulimit    : Positive;
        output    : String_Vector.Vector;
    
    begin
        ulimit := length(input);
        for i in 1 .. ulimit loop
            if element(input, i) =  '"' and is_quoted = false then
                is_quoted := true;
            elsif element(input, i) = ' ' and is_quoted = false then
                output.append(slice(input, lastpos, i));
                lastpos := i;
            end if;
        end loop;
        output.append(slice(input, lastpos, ulimit));
        return output;
    end tokenize;

    -- convert ada string to something understoood by execvp()
    function convertstrings(input : in String) 
    return Interfaces.C.Strings.chars_ptr is
        output : Interfaces.C.Strings.chars_ptr;
        t      : Interfaces.C.char_array := Interfaces.C.to_c(input);
    begin
        output := Interfaces.C.Strings.new_char_array(t);
        return output;
    end convertstrings;


    function createargs(input : in String_Vector.Vector) 
    return Interfaces.C.Strings.chars_ptr_array is
        argc :  Interfaces.C.size_t := Interfaces.C.size_t(length(input));
        args : Interfaces.C.Strings.chars_ptr_array(0 .. argc);
        i    : Interfaces.C.size_t := 0;
    begin
        for each of input loop
            args(i) := convertstrings(each);
            if (argc = i) then
                put_line("warning overwriting");
            end if;
        end loop;
        args(argc) := Interfaces.C.Strings.Null_Ptr;
        return args;
    end createargs;

    -- execute user input 
    procedure execute (input : in Unbounded_String) is
        use type Interfaces.C.int;
        tokens : String_Vector.Vector               := tokenize(input);
        args : Interfaces.C.Strings.chars_ptr_array := createargs(tokens);
        is_bg  : Boolean := false;
        child        : Interfaces.C.int;
        child_status : aliased Interfaces.C.int;
    begin
        child := fork;
        if child < 0 then
            put_line("fork failed");
        elsif child = 0 then
            execvp(args(0), to_ptr(args'address));
        elsif not is_bg then
            loop 
                exit when Wait(child_status'access) = child;
            end loop;
        end if;
    end execute;

continue  : Boolean := true;
userinput : Unbounded_String;
tokens    : String_Vector.Vector;
history   : String_Vector.Vector;
begin
    while continue loop
        put("ash>");
        userinput := get_line;
        if userinput = "exit" or userinput = "quit" then
            continue := false;
        elsif
            userinput = "history" then
            for each of history loop
                put_line(each);
            end loop;
        end if;
        execute(userinput);
        append(history, to_string(userinput));
    end loop;
end main;

